/*  -*-mode: c++-*-
 * Gionata Massi <gionata.massi@gmail.com>
 * Febbraio 2007
 *
 * File: aga.cpp
 *
 * Questo file fa parte di 'aga'
 */

/*!
  \file		aga.cpp
  \brief	Solutore MOSP e MOSPk mediante algoritmo genetico.
  \author	Gionata Massi
  \version	2009-07-23
  \date		2007-03-22

  Entry point, parser della riga di comando e help.
 */

#include "aga.h"

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int verbose = 0;
int xml_output = 0;

void parse_cmd_line(int, char **, GeneticData *, char **);
char *instance_to_string(GeneticData * data, char **instance_string);
void set_configuration_param(param * param, param_type type,
                             const char *identifier, const char *desc,
                             void *reference);
void initializeConfigurationTable(GeneticData * data);
void output_verbose(GeneticData * data);
void output_xml(GeneticData * data, int argc, char *argv[], clock_t start,
                clock_t end);
void output_csv(GeneticData * data, clock_t start, clock_t end);

void initializeConfigurationTable(GeneticData * data)
{
	param aga_param[] = {
		{integer, "LocSearchNumBestInd", "", &(data->LocSearchNumBestInd)}, //1
		{integer, "LocSearchDepth", "", &(data->LocSearchDepth)}, //2
		{floating_point, "FirstParent", "", &(data->FirstParent)}, //3
		{integer, "LocSearchFreq", "", &(data->LocSearchFreq)}, //3
		{boolean, "LocSearchSteepDesc", "", &(data->LocSearchSteepDesc)},//5
		{integer, "LocSearchNumTotInd", "",	&(data->LocSearchNumTotInd)}, //6
		{integer, "LocSearchNeighWidth", "", &(data->LocSearchNeighWidth)}, //7
		{floating_point, "ProbCrossover", "", &(data->ProbCrossover)}, //8
		{floating_point, "HeuristicsProportion", "", &(data->HeuristicsProportion)}, //9
		{integer, "NumGenerations", "", &(data->NumGenerations)}, //10
		{integer, "Elite", "", &(data->Elite)}, //11
		{floating_point, "InitialMutation", "", &(data->InitialMutation)}, //12
		{integer, "NumIndividuals", "", &(data->NumIndividuals)}, //13
		{floating_point, "PhaseHop[0]", "", &(data->PhaseHop[0])}, //14
		{floating_point, "PhaseHop[1]", "", &(data->PhaseHop[1])}, //15
		{integer, "SelectionRounds", "", &(data->SelectionRounds)}, //16
		{unsigned_int, "seed", "", &(data->seed)}, //17
		{floating_point, "Weight[0][TOTAL_COST]", "", &(data->Weight[0][TOTAL_COST])}, //18
		{floating_point, "Weight[0][VEHICLES_NUMBER]", "", &(data->Weight[0][VEHICLES_NUMBER])}, //19
		{floating_point, "Weight[1][TOTAL_COST]", "", &(data->Weight[1][TOTAL_COST])}, //20
		{floating_point, "Weight[1][VEHICLES_NUMBER]", "", &(data->Weight[1][VEHICLES_NUMBER])}, //21
		{floating_point, "Weight[2][TOTAL_COST]", "", &(data->Weight[2][TOTAL_COST])}, //22
		{floating_point, "Weight[2][VEHICLES_NUMBER]", "", &(data->Weight[2][VEHICLES_NUMBER])}, //23
		{chars_string, "Instance", "", &(data->name)} //24
	};
	data->configuration.size = sizeof(aga_param) / sizeof(param);
	data->configuration.table =
	    (param *) calloc(data->configuration.size, sizeof(param));
	for (int p = 0; p < data->configuration.size; p++) {
		set_configuration_param(data->configuration.table + p,
		                        aga_param[p].type,
		                        aga_param[p].identifier,
		                        aga_param[p].desc,
		                        aga_param[p].reference);
	}
}

//! Main
/*!
  I parametri da passare al main sono:

	\param argc numero di argomenti
	\param argv vettore degli argomenti
*/
int main(int argc, char **argv)
{
	char *szFilename = NULL;
	GeneticData data;
	clock_t start, end;
	int errCode = EXIT_SUCCESS;
	int n = 1;

	initializeConfigurationTable(&data);

	parse_cmd_line(argc, argv, &data, &szFilename);

	SetDefaults(&data);

	/* LETTURA DATI */
	if (!szFilename[0]) {
		fputs("ERRORE: file di input non indicato\n", stderr);
		errCode = 1;
		goto uscita;
	}
	ReadData(&data, szFilename);

	free(szFilename);

	// Scrivo a video l'istanza letta
	/*
	for (std::vector <Customer *>::const_iterator c = data.CustomerSet.begin();
		c != data.CustomerSet.end(); c++)
			cout << "Customer " << (*c)->Number << " (" << (*c)->ReadyTime << ", " << (*c)->DueDate << ")" << endl;
	*/

	//preprocess(&data); ???

	fitness_initializer(&data);

	if (!data.NumIndividuals) {
		data.NumIndividuals = 20;	// 10 * data.NumGenes;
	}
	AllocateIndividual(&(data.TheBestOne), data.NumGenes);
	AllocateIndividual(&(data.BestVehicleInd), data.NumGenes);

	data.People = AllocatePopulation(data.NumIndividuals, data.NumGenes);
	if (!data.People) {
		fprintf(stderr, "ERRORE: imposibile creare la popolazione\n");
		errCode = 7;
		goto uscita;
	}
	if (!data.NumGenerations) {
		/* Numero di generazioni per terminare */
		data.NumGenerations = 500;	// 5 * data.NumIndividuals;
	}
	// data.BestValue.score = 100.0;

	/* Inizializza il generatore di sequenza pseudo-casuale */
	srand(data.seed);

	if (data.LocSearchNeighWidth == 0)
		data.LocSearchNeighWidth = data.NumIndividuals;

	start = clock();

	/* Algoritmo Genetico */
	GeneticOptimizer(&data);

	/* Prende il tempo di fine ottimizzazione */
	end = clock();

	//FreeMetricsMatrices(&data);

	//fitness_finalizer();

	/* PostProcessing */
	//postprocess(&data); ???

	/* OUTPUT SOLUZIONE */
	if (verbose)
		output_verbose(&data);
	else if (xml_output)
		output_xml(&data, argc, argv, start, end);
	else
		output_csv(&data, start, end);

uscita:
	putchar('\n');

	//FreeIndividual(&(data.BestVehicleInd));
	//FreeIndividual(&(data.TheBestOne));

	free(data.configuration.table);
	//FreeData(&data);

	// system("pause");

	return errCode;
}

//! Stampa come richiamare il programma
void Usage(const char *prg)
{
	const char *params[] = {
		"   -a La prima riga del file contiene il nome dell'istanza",
		"   -c Probabilita_Crossover",
		"   -e Rapporto_Popolazione_iniziale_eurista_totale",
		"   -f Funzione_di_Fitness",
		"   -g Numero_Generazioni_fitness_costante",
		"   -h",
		"   -k Numero_schemi_attesa",
		"   -m Probabilita_Mutazione",
		"   -n Numero_Individui",
		"   -r Numero_Rounds_per_selezionare_individuo",
		"   -s Seme",
		"   -v",
		"   -x Output in XML",
		NULL
	};
	const char *descs[] = {
		"   -a La prima riga del file contiene il nome dell'istanza",
		"   -c stabilisce la probabilita' di crossover, in [0..1]",
		"   -e rapporto popolazione iniziale da eurista/totale, in [0..1]",
		"   -f seleziona la fitness, fra {MOSPk, MOSP, MOSPN}",
		"   -g imposta il numero di generazioni",
		"   -h stampa a video questo aiuto",
		"   -k imposta il numero di attese del MOSPk",
		"   -m imposta la probabilita' di mutazione, in [0..1]",
		"   -n imposta il numero di individui della popolazione",
		"   -r numero di tornei per selezionare l'individuo",
		"      destinato a riprodursi",
		"   -s seme del generatore di numeri pseudo-casuali",
		"   -v output in modalita' verbosa",
		"   -x Output in XML",
		NULL
	};
	int i;

	printf
	("Algoritmo genetico per problemi di sequenziamento degli schemi di taglio\n\n");
	printf("Sintassi:\n   %s [parametri] filename\n\nParametri:\n", prg);
	i = 0;
	while (params[i]) {
		printf("%s\n", params[i++]);
	}
	printf("\nDescrizione:\n");
	i = 0;
	while (descs[i]) {
		printf("%s\n", descs[i++]);
	}
	printf("\n");
}

void parse_cmd_line(int argc, char **argv, GeneticData * data, char **Filename)
{
	int n;

	if (argc != data->configuration.size + 1) {
		cerr << "Usage:" << endl;
		for (int i = 0; i < data->configuration.size - 1; i++)
			cerr << data->configuration.table[i].identifier << " ";
		cerr << data->configuration.table[data->configuration.size - 1].identifier << endl;
		exit(1);
	}

	for (n = 0; n < data->configuration.size; n++) {
		switch (data->configuration.table[n].type) {
		case integer:
			*((int *)data->configuration.table[n].reference) =
			    (int)strtol(argv[n + 1], NULL, 10);
			break;
		case long_int:
			*((long *)data->configuration.table[n].reference) =
			    strtol(argv[n + 1], NULL, 10);
			break;
		case unsigned_int:
			*((unsigned *)data->configuration.table[n].reference) =
			    strtol(argv[n + 1], NULL, 10);
			break;
		case floating_point:
			*((double *)data->configuration.table[n].reference) =
			    strtod(argv[n + 1], NULL);
			break;
		case boolean:
			*((bool *) data->configuration.table[n].reference) =
			    (strtol(argv[n + 1], NULL, 10) != 0);
			break;
		case chars_string:
			*Filename = (char *)malloc(strlen(argv[n + 1]) + 1);
			strcpy(*Filename, argv[n + 1]);
			// cerca l'ultimo slash
			const char *sip;
			for (sip = argv[n + 1] + strlen(argv[n + 1]);
			     sip > argv[n + 1] && *(sip - 1) != '/'
			     && *(sip - 1) != '\\'; sip--) {
			}
			const char *sfp;
			int len;
			for (sfp = sip, len = 0; *sfp != '\0' && *sfp != '.';
			     sfp++, len++) {
			}
			strncpy(((char *)data->configuration.table[n].
			         reference), sip, len);
			((char *)data->configuration.table[n].reference)[len] =
			    0;
			break;
			/*default:
			   break; */
		}
	}
}

void parse_cmd_line2(int argc, char **argv, GeneticData * data, char **Filename)
{
	char szFilename[FILENAME_MAX];
	double dbl;
	//char *fit_fun_name = NULL;    /*< MOSP, MOSPk */
	int n = 1;
	szFilename[0] = 0;

	/* il parser della riga di comando */
	{
		for (n = 1; n < argc; ++n) {
			char *ptr = argv[n];

			if (*ptr == '-') {
				++ptr;
				switch (*ptr) {

				case 'B':
					data->LocSearchNumBestInd =
					    strtol(ptr + 1, &ptr, 10);
					break;
				case 'D':
					data->LocSearchDepth =
					    strtol(ptr + 1, &ptr, 10);
					break;
				case 'F':
					data->FirstParent =
					    strtod(ptr + 1, &ptr);
					break;
				case 'Q':
					data->LocSearchFreq =
					    strtol(ptr + 1, &ptr, 10);
					break;
				case 'S':
					data->LocSearchSteepDesc = true;
					break;
				case 'T':
					data->LocSearchNumTotInd =
					    strtol(ptr + 1, &ptr, 10);
					break;
				case 'W':
					data->LocSearchNeighWidth =
					    strtol(ptr + 1, &ptr, 10);
					break;
				case 'a':	/* Le prime righe del file contenfono il nome dell'istanza */
					data->name_flag = 1;
					break;
				case 'c':	/* Probabilita' di Crossover */
					dbl = strtod(ptr + 1, &ptr);
					if (dbl < 0.0 || dbl > 1.0) {
						fputs
						("ATTENZIONE: probabilita' di crossover errata\n",
						 stderr);
					} else {
						data->ProbCrossover = dbl;
					}
					break;
				case 'e':	/* Uso di euristiche */
					dbl = strtod(ptr + 1, &ptr);
					if (dbl < 0.0 || dbl > 1.0) {
						fputs
						("ATTENZIONE: rapporto di popolazione 'euristica' iniziale errato\n",
						 stderr);
					} else {
						data->HeuristicsProportion =
						    dbl;
					}
					break;
				case 'f':	/* Funzione di Fitness */
					++ptr;
					if (!*ptr) {
						ptr = argv[++n];
					}
					/*					if (*ptr) {
											fit_fun_name = ptr;
										    if (!strcasecmp
												(fit_fun_name,
												 "MOSP")) {
												data->FitFunPtr =
												    0;
											} else
												fputs
												    ("ATTENZIONE: funzione di fitness non riconoscuta\n",
												     stderr);
										}
					*/
					break;
				case 'g':	/* Numero di generazioni */
					++ptr;
					if (!*ptr) {
						ptr = argv[++n];
					}
					if (*ptr) {
						data->NumGenerations =
						    strtol(ptr, &ptr, 10);
					}
					break;
				case 'h':	/* Informazioni d'uso */
					Usage(argv[0]);
					exit(0);
				case 'l':	/* Elite */
					++ptr;
					if (!*ptr) {
						ptr = argv[++n];
					}
					if (*ptr) {
						data->Elite =
						    strtol(ptr, &ptr, 10);
					}
					break;
				case 'm':	/* Probabilita' di Mutazione */
					dbl = strtod(ptr + 1, &ptr);
					if (dbl < 0.0 || dbl > 1.0) {
						fputs
						("ATTENZIONE: probabilita' di mutazione errata\n",
						 stderr);
					} else {
						data->InitialMutation =
						    data->ProbMutation = dbl;
					}
					break;
				case 'n':	/* Numero di individui */
					++ptr;
					if (!*ptr) {
						ptr = argv[++n];
					}
					if (*ptr) {
						data->NumIndividuals =
						    strtol(ptr, &ptr, 10);
					}
					break;
				case 'p':	/* phase hops */
					for (int i = 0; i < PHASES - 1; i++) {
						data->PhaseHop[i] =
						    strtol(ptr + i + 1, &ptr,
						           10);
					}
					break;
				case 'r':	/* Numero di Rounds per selezionare
						 * un individuo */
					data->SelectionRounds =
					    strtol(ptr + 1, &ptr, 10);
					break;
				case 's':	/* Seme del generatore di numeri
						 * pseudo-casuali */
					++ptr;
					if (!*ptr) {
						ptr = argv[++n];
					}
					if (*ptr) {
						data->seed =
						    (unsigned)strtol(ptr, &ptr,
						                     10);
					}
					break;
				case 't':	/* Tempo limite */
					++ptr;
					if (!*ptr) {
						ptr = argv[++n];
					}
					if (*ptr) {
						data->timeLimit = atoi(ptr);
					}
					break;
				case 'v':	/* Output verboso */
					verbose = 1;
					break;
				case 'w':	/* Pesi */
					for (int i = 0; i < PHASES; i++) {
						for (int j = 0; j < parameters;
						     j++) {
							data->Weight[i][j] =
							    strtod(ptr + 1,
							           &ptr);
						}
					}
					break;
				case 'x':	/* Output XML */
					xml_output = 1;
					break;
				default:
					fprintf(stderr,
					        "ATTENZIONE: parametro -%c non gestito\n",
					        *ptr);
				}
			} else {
				/* filename */
				char *e = szFilename + FILENAME_MAX - 1;
				char *s = szFilename;

				if (*ptr == '"') {
					++ptr;
					while (*ptr && *ptr != '"' && s != e) {
						*s = *ptr;
						++s;
						++ptr;
					}
				} else {
					while (*ptr && *ptr != ' ' && s != e) {
						*s = *ptr;
						++s;
						++ptr;
					}
				}
				*s = 0;
			}
		}
	}

	*Filename = (char *)malloc(strlen(szFilename) + 1);
	strcpy(*Filename, szFilename);
}

char *instance_to_string(GeneticData * data, char **instance_string)
{
	/*int i, j, pos = 0;
	(*instance_string) =
	    (char *)malloc(data->NumGenes * (data->NumPieces * 2 + 1) + 1);

	for (i = 0; i < data->NumGenes; i++) {
		for (j = 0; j < data->NumPieces - 1; j++) {
			sprintf(*instance_string + pos, "%d ", data->DM[i][j]);
			pos += 2;
		}
		sprintf(*instance_string + pos, "%d\n",
			data->DM[i][data->NumPieces - 1]);
		pos += 2;
	}
	*/
	return *instance_string;
}

void set_configuration_param(param * param, param_type type,
                             const char *identifier, const char *desc,
                             void *reference)
{
	param->type = type;
	if (identifier)
		strncpy(param->identifier, identifier, 50);
	else
		param->identifier[0] = '\0';
	if (desc)
		strncpy(param->desc, desc, 50);
	else
		param->desc[0] = '\0';
	param->reference = (void *)reference;
#ifdef VERBOSE_DEBUG
	printf("%s\t: ");
	switch (type) {
	case integer:
		printf("%d\n", *((int *)reference));	// printf("i: %i (%s)\n", i, data->configuration[i].identifier); fflush(NULL);
		break;
	case long_int:
		printf("%ld\n", *((long *)reference));
		break;
	case unsigned_int:
		printf("%u\n", *((unsigned *)reference));
		break;
	case floating_point:
		printf("%f\n", *((double *)reference));
		break;
	case boolean:
		printf("%i\n", (int)*((bool *) reference));
		break;
	}
#endif
}

void output_verbose(GeneticData * data)
{

	/*
			printf("Solution: %d\nPattern list:",
			       data->BestMosInd.Score.measure[MOS]);
			for (n = 0; n < data->NumGenes; ++n) {
				printf(" %d", data->BestMosInd.Sequence[n]);
			}
			putchar('\n');
			putchar('\n');
			// Stampa i pattern
			for (int i = 0; i < data->NumGenes; i++) {
				for (n = 0; n < data->NumPieces - 1; n++) {
					printf("%d ",
					       data->DM[data->BestMosInd.Sequence[i]]
					       [n]);
				}
				printf("%d\n", data->DM[data->BestMosInd.Sequence[i]][n]);
			   if (i == 0)
				   putchar('\n');
				   else {
				   printf(" N:%3d C:%3d O:%3d\n",
				   data->
				   MeasureMatrix[MATRIX_NEW][data->
				   TheBestOne.Sequence
				   [i -
				   1]]
				   [data->TheBestOne.Sequence[i]],
				   data->MeasureMatrix[MATRIX_COMMON]
				   [data->TheBestOne.Sequence[i - 1]]
				   [data->TheBestOne.Sequence[i]]//,
				   //                                          data->BestValue.openStacks[i]
				   );
				   }
			}

			// Stampa su stderr tutti i parametri dell'algoritmo
			fprintf(stderr, "\nCommand line: ");
			for (n = 0; n < argc; ++n)
				fprintf(stderr, "%s ", argv[n]);
			fprintf(stderr, "\n");
			fprintf(stderr, "Parametri:\n");
			fprintf(stderr,
				"\tSeme di srand              : %u\n", data->seed);
			fprintf(stderr,
				"\tFunzione di fitness        : %s\n", fit_fun_name);
			fprintf(stderr,
				"\tProbabilita' di crossover  : %g\n",
				data->ProbCrossover);
			fprintf(stderr,
				"\tProbabilita' di mutazione  : %g\n",
				data->ProbMutation);
			fprintf(stderr,
				"\tPercentuale popolazione eur: %g%%\n",
				data->HeuristicsProportion * 100);
			fprintf(stderr,
				"\tNumero di articoli distinti: %d\n", data->NumPieces);
			fprintf(stderr,
				"\tNumero di schemi ( geni )  : %d\n", data->NumGenes);
			fprintf(stderr,
				"\tNumero di individui        : %d\n",
				data->NumIndividuals);
			fprintf(stderr,
				"\tNumero di generazioni      : %d\n",
				data->NumGenerations);
			fprintf(stderr,
				"\tNumero di round            : %d\n",
				data->SelectionRounds);
			fprintf(stderr,
				"\tNumero di schemi di attesa : %d\n\n",
				data->NumEmptyPatterns);

			fprintf(stderr, "Bounds:\n");
			fprintf(stderr,
				"\tMOS: (%3d - %3d)\n", data->LB[MOS], data->UB[MOS]);
			fprintf(stderr,
				"\tTOS: (%3d - %3d)\n\n", data->LB[TOS], data->UB[TOS]);

			fprintf(stderr, "Risultato:\n");
			fprintf(stderr,
				"\t(MOS - TOS): (%3d - %3d)\n",
				data->BestMosInd.Score.measure[MOS],
				data->BestMosInd.Score.measure[TOS]);
			fprintf(stderr, "\nTempo di esecuzione: %g secondi\n",
				(double)(end - start) / CLOCKS_PER_SEC);
		}
	*/
}

void output_xml(GeneticData * data, int argc, char *argv[], clock_t start,
                clock_t end)
{
	/*
	char *matrix_string = 0;
	printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
	       "<MOSP xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"test_mp.xsd\">\n"
	       "\t<instance_name>%s</instance_name>\n"
	       "\t<original_instance>\n"
	       "\t\t<production_matrix>\n"
	       "\t\t\t<size>\n"
	       "\t\t\t\t<products>%d</products>\n"
	       "\t\t\t\t<customers>%d</customers>\n"
	       "\t\t\t</size>\n"
	       "\t\t\t<data row=\"product\">\n"
	       "%s"
	       "\t\t\t</data>\n"
	       "\t\t</production_matrix>\n",
	       data->name,
	       data->NumGenes,
	       data->NumPieces, instance_to_string(data, &matrix_string));
	free(matrix_string);
	printf("\t\t<lower_bounds>\n"
	       "\t\t\t<lower_bound description=\"?\">%d</lower_bound>\n"
	       "\t\t</lower_bounds>\n", data->LB[MOS]);
	printf("\t\t<upper_bounds>\n"
	       "\t\t\t<upper_bound description=\"Number of item types.\">%d</upper_bound>\n"
	       "\t\t</upper_bounds>\n", data->UB[MOS]);
	char time_buffer[31];
	struct tm *now;
	time_t t;
	time(&t);
	now = gmtime(&t);
	strftime(time_buffer, 30, "%Y-%m-%dT%H:%M:%SZ", now);
	printf
	    ("\t\t<solution algorithm=\"Luigi De Giovanni - Gionata Massi - Ferdinando Pezzella\" "
	     "lower_bound=\"%d\" " "upper_bound=\"%d\" "
	     "timestamp=\"%s\" " "iteration=\"%d\" "
	     "run_time=\"%g\" >\n" "\t\t\t<MOS>%d</MOS>\n"
	     "\t\t\t<sequence>", data->LB[MOS]
	     , data->UB[MOS]
	     , time_buffer, data->NumGenerations,
	     (double)(end - start) / (CLOCKS_PER_SEC / 1000)
	     , data->BestMosInd.Score.measure[MOS]
	    );
	for (int b = 0; b < data->NumGenes - 1; ++b) {
		printf("%d ", data->BestMosInd.Sequence[b]);
	}
	printf("%d"
	       "</sequence>\n"
	       "\t\t</solution>\n",
	       data->BestMosInd.Sequence[data->NumGenes - 1]);
	printf("\t</original_instance>\n" "\t<options" " command_line=\"");
	for (int a = 1; a < argc - 1; a++) {
		printf("%s ", argv[a]);
	}
	printf("%s\">\n", argv[argc - 1]);
	//....
	printf("\t</options>\n" "</MOSP>\n");
	*/
}

void output_csv(GeneticData * data, clock_t start, clock_t end)
{

#define SEP ","

#ifdef PRINT_CSV_HEADER
	printf("Instance"
	       SEP "Customers"
	       SEP "Max_Vehicles"
	       SEP "Total_cost"
	       SEP "Routes"
	       SEP "Total time"
	       SEP "Pre-processing time"
	       SEP "Initial population time"
	       SEP "Evolution cycle time"
	       SEP "Post-processing time"
	       SEP "Gen/LS"		// Chi ha trovato la soluzione a mos minore (Gen = 0, LS = 1)
	       SEP "Terminating generation"		// Generazione a cui ha terminato
	       SEP "Last total_cost update"		// Generazione in cui ha aggiornato il mos per l'ultima volta
	       SEP "Fitness evaluations"		// Numero di valutazioni della fitness
	       SEP "Local Search max depth reached" // Massima profindita' raggiunta dalla ricerca locale
	       SEP "Best total_cost at constructive heuristics phase"
	       SEP "Best total_cost at LS on heuristic inds"
	       SEP "Best total_cost at phase 1"
	       SEP "Best total_cost at phase 2"
	       SEP "Best total_cost at phase 3"
	       SEP "Sequence"
	      );
	for (int i = 0; i < data->configuration.size - 1; i++)
		printf(SEP "%s", data->configuration.table[i].identifier);
	printf(SEP "Algorithm" SEP  "Version\n");
#endif
	printf("%s"	SEP 	// Instance
	       "%d"	SEP 	// # Customers
	       "%d"	SEP 	// # Max_Vehicles
	       , data->name
	       , data->NumGenes
	       , data->NumVehicles
	      );
	// Solution performance indicators
	printf("%f"	SEP 	// Total_cost
	       "%f"	SEP 	// Routes
	       "%f"	SEP 	// Tempo totale
	       "%f"	SEP 	// Tempo preprocessing
	       "%f"	SEP 	// Tempo popolazione iniziale
	       "%f"	SEP 	// Tempo ciclo evolutivo
	       "%f"	SEP 	// Tempo postprocessing
	       "%d"	SEP 	// Chi ha trovato la soluzione a mos minore (Gen = 0, LS = 1)
	       "%d"	SEP 	// Generazione a cui ha terminato
	       "%d"	SEP 	// Generazione in cui ha aggiornato il total_cost per l'ultima volta
	       "%ld" SEP 	// Numero di valutazioni della fitness
	       "%d"	SEP 	// Massima profondita' della ricerca locale
	       , data->TheBestOne.Score.measure[TOTAL_COST]
	       , data->TheBestOne.Score.measure[VEHICLES_NUMBER]
	       , ((double)(end + 0.0) - start) / CLOCKS_PER_SEC,
	       ((double)(data->stat.elaps[PREPROCESSING].end + 0.0) -
	        data->stat.elaps[PREPROCESSING].start) / CLOCKS_PER_SEC,
	       ((double)(data->stat.elaps[INITIAL_POPULATION].end + 0.0)
	        -
	        data->stat.elaps[INITIAL_POPULATION].start) /
	       CLOCKS_PER_SEC,
	       ((double)(data->stat.elaps[REPRODUCTION_CYCLE].end + 0.0)
	        -
	        data->stat.elaps[REPRODUCTION_CYCLE].start) /
	       CLOCKS_PER_SEC,
	       ((double)(data->stat.elaps[POSTPROCESSING].end + 0.0) -
	        data->stat.elaps[POSTPROCESSING].start) / CLOCKS_PER_SEC,
	       data->stat.ls_found_opt, data->CurrentGeneration,
	       data->stat.last_best_generation[TOTAL_COST],
	       data->stat.fitness_evaluations,
	       data->stat.ls_max_depth);
	// Mos durante le varie fasi
	for (int p = 0; p < PHASES + 2; p++)
		printf("%f" SEP , data->stat.best_found_at_phase[p][TOTAL_COST]);
	// Sequenza
	for (int k = 0; k < data->NumGenes - 1; k++)
		printf("%d ", data->TheBestOne.Sequence[k]);
	printf("%d" SEP, data->TheBestOne.Sequence[data->NumGenes - 1]);

	// Parameters
	for (int i = 0; i < data->configuration.size; i++) {
		switch (data->configuration.table[i].type) {
		case integer:
			printf("%d", *((int *)data->configuration.table[i].reference));
			break;
		case long_int:
			printf("%ld",
			       *((long *)data->configuration.table[i].
			         reference));
			break;
		case unsigned_int:
			printf("%u",
			       *((unsigned *)data->configuration.table[i].
			         reference));
			break;
		case floating_point:
			printf("%f",
			       *((double *)data->configuration.table[i].
			         reference));
			break;
		case boolean:
			printf("%i",
			       (int)*((bool *) data->configuration.table[i].
			              reference));
			break;
		case chars_string:
			printf("%s",
			       (char *)data->configuration.table[i].reference);
			break;
		default:
			break;
		}
		if (i == data->configuration.size - 2) {
			break;
		} else {
			printf(SEP);
		}
	}
	printf(SEP xstr(ALGORITHM) SEP  xstr(VERSION));

}
